package com.example.demo.generator.scaffold.controller;

import com.example.demo.common.BaseResponse;
import com.example.demo.common.ResultUtils;
import com.example.demo.generator.scaffold.dto.GeneratedFileVO;
import com.example.demo.generator.scaffold.dto.ScaffoldGenerationRequest;
import com.example.demo.generator.scaffold.service.ScaffoldGenerationLogService;
import com.example.demo.generator.scaffold.service.ScaffoldGenerationService;
import com.example.demo.generator.scaffold.util.ZipPackagingService;
import com.example.demo.config.security.UserPrincipal;
import com.example.demo.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;
import java.util.List;

/**
 * 脚手架生成接口。
 */
@RestController
@RequestMapping("/generator/scaffold")
@RequiredArgsConstructor
@Slf4j
@Validated
@Tag(name = "脚手架生成", description = "根据建表 SQL 生成后端 CRUD 及相关文件")
public class ScaffoldController {

    private final ScaffoldGenerationService scaffoldGenerationService;
    private final ZipPackagingService zipPackagingService;
    private final ObjectProvider<UserService> userServiceProvider;
    private final ObjectProvider<ScaffoldGenerationLogService> logServiceProvider;

    @PostMapping("/files")
    @Operation(summary = "生成文件列表", description = "返回各模板渲染后的内容，可用于预览或下载")
    public BaseResponse<List<GeneratedFileVO>> generateAsFiles(@Valid @RequestBody ScaffoldGenerationRequest request) throws Exception {
        List<GeneratedFileVO> files = scaffoldGenerationService.generateMybatisPlusCrudView(request);
        return ResultUtils.success(files);
    }

    @PostMapping("/zip")
    @Operation(summary = "生成压缩包", description = "生成脚手架 ZIP 包并返回下载路径")
    public BaseResponse<ZipPackagingService.ZipResult> generateAsZip(@Valid @RequestBody ScaffoldGenerationRequest request,
                                                                     HttpServletRequest httpRequest) throws Exception {
        Long userId = resolveCurrentUserId();
        userServiceProvider.ifAvailable(service -> {
            if (userId != null) {
                service.validateZipQuota(userId);
            }
        });

        ZipPackagingService.ZipResult result = zipPackagingService.generateZip(
                scaffoldGenerationService.generateMybatisPlusCrud(request),
                buildDownloadUrl(httpRequest)
        );
        try {
            userServiceProvider.ifAvailable(service -> {
                if (userId != null) {
                    service.consumeZipQuota(userId);
                }
            });
            logServiceProvider.ifAvailable(service ->
                    service.recordGeneration(request, result, resolveClientIp(httpRequest), userId));
        } catch (RuntimeException ex) {
            cleanupOnFailure(result);
            throw ex;
        }
        return ResultUtils.success(result);
    }

    @GetMapping("/download/{zipName}")
    @Operation(summary = "下载压缩包", description = "根据 ZIP 文件名返回压缩包内容")
    public ResponseEntity<FileSystemResource> downloadZip(@PathVariable String zipName) {
        Path zipPath = zipPackagingService.resolveZipPath(zipName);
        FileSystemResource resource = new FileSystemResource(zipPath);
        if (!resource.exists()) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + resource.getFilename())
                .contentLength(resource.getFile().length())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }

    private String buildDownloadUrl(HttpServletRequest request) {
        String scheme = request.getScheme();
        String host = request.getServerName();
        int port = request.getServerPort();
        return scheme + "://" + host + (port == 80 || port == 443 ? "" : ":" + port) + "/generator/scaffold/download/";
    }

    private String resolveClientIp(HttpServletRequest request) {
        String forwarded = request.getHeader("X-Forwarded-For");
        if (StringUtils.hasText(forwarded)) {
            return forwarded.split(",")[0].trim();
        }
        String realIp = request.getHeader("X-Real-IP");
        if (StringUtils.hasText(realIp)) {
            return realIp;
        }
        return request.getRemoteAddr();
    }
    private void cleanupOnFailure(ZipPackagingService.ZipResult result) {
        if (result == null) {
            return;
        }
        try {
            if (result.getZipFile() != null) {
                Files.deleteIfExists(result.getZipFile());
            }
            if (result.getWorkspace() != null && Files.exists(result.getWorkspace())) {
                try (Stream<Path> paths = Files.walk(result.getWorkspace())) {
                    paths.sorted(Comparator.reverseOrder())
                            .forEach(path -> {
                                try {
                                    Files.deleteIfExists(path);
                                } catch (IOException e) {
                                    log.debug("删除临时目录失败: {}", e.getMessage());
                                }
                            });
                }
            }
        } catch (IOException ex) {
            log.debug("清理 ZIP 临时文件失败: {}", ex.getMessage());
        }
    }

    private Long resolveCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return null;
        }
        Object principal = authentication.getPrincipal();
        if (principal instanceof com.example.demo.config.security.UserPrincipal) {
            return ((com.example.demo.config.security.UserPrincipal) principal).getId();
        }
        return null;
    }
}
